package com.googlecode.horncomb.web.spring.tags;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.jsp.JspException;

import org.springframework.util.Assert;
import org.springframework.web.servlet.tags.form.AbstractHtmlInputElementTag;
import org.springframework.web.servlet.tags.form.CheckboxTag;
import org.springframework.web.servlet.tags.form.InputTag;
import org.springframework.web.servlet.tags.form.PasswordInputTag;
import org.springframework.web.servlet.tags.form.RadioButtonTag;
import org.springframework.web.servlet.tags.form.TagWriter;
import org.springframework.web.servlet.tags.form.TextareaTag;

import com.googlecode.horncomb.util.lang.BeanUtils;

public class DynamicInputTag extends AbstractHtmlInputElementTag {
	private static final long serialVersionUID = 4600796440994306927L;

	private String type = "text";

	private static Map<String, Class<?>> tagTypeCache = new HashMap<String, Class<?>>();
	static {
		tagTypeCache.put("text", InputTag.class);
		tagTypeCache.put("password", PasswordInputTag.class);
		tagTypeCache.put("textarea", TextareaTag.class);
		tagTypeCache.put("select", SelectTag.class);
		tagTypeCache.put("radio", RadioButtonTag.class);
		tagTypeCache.put("radioGroup", RadioButtonGroupTag.class);
		tagTypeCache.put("checkbox", CheckboxTag.class);
		tagTypeCache.put("checkboxGroup", CheckboxGroupTag.class);
	}
	// input text
	private String size; // text, select
	private String maxlength;
	private String alt;
	private String onselect; // text, textarea
	private String autocomplete;

	// textarea
	private String rows;
	private String cols;

	// password
	private boolean showPassword = false;

	// checkbox, radio
	private Object value;

	public void setBoundValue() {

	}

	// select
	/**
	 * Indicates whether or not the '<code>select</code>' tag allows
	 * multiple-selections.
	 */
	private Object multiple = Boolean.FALSE;
	/** 是否允许首行为空 */
	private String firstEmpty = "true";

	// checkboxGroup, radioGroup
	private Object items; // select, checkboxGroup, radioGroup
	/** 值属性名 */
	private String itemValue; // select, checkboxGroup, radioGroup
	/** 文本标签属性名 */
	private String itemLabel; // select, checkboxGroup, radioGroup
	/** 变量名 */
	private String varStatus;
	private String varValue;
	private String varLabel = "label";
	/** 布局类型 */
	private String layout = AbstractCheckableInputGroupTag.LAYOUT_VERTICAL;
	private String evalLastBody = "false";

	private AbstractHtmlInputElementTag innerTag = null;

	@Override
	protected int writeTagContent(TagWriter tagWriter) throws JspException {
		// String commandName = (String) this.pageContext
		// .getAttribute(FormTag.COMMAND_NAME_VARIABLE_NAME);
		// // 修改默认commandName
		// if (!StringUtils.hasText(commandName)
		// || commandName.equals(FormTag.DEFAULT_COMMAND_NAME)) {
		// this.pageContext.setAttribute(FormTag.COMMAND_NAME_VARIABLE_NAME,
		// "form");
		// }

		// 获取标签类型
		Class<?> tagType = tagTypeCache.get(this.getType());
		if (tagType == null) {
			throw new JspException(
					"the value of attribute type is invalid, it must match "
							+ tagTypeCache.keySet());
		}
		try {
			this.innerTag = (AbstractHtmlInputElementTag) tagType.newInstance();
		} catch (InstantiationException e) {
			throw new JspException("create innerTag[" + tagType.getName()
					+ "] instance fail", e);
		} catch (IllegalAccessException e) {
			throw new JspException("create innerTag[" + tagType.getName()
					+ "] instance fail", e);
		}
		BeanUtils.copyPropertiesAsPossible(this, this.innerTag);
		return this.innerTag.doStartTag();
	}

	@Override
	public int doAfterBody() throws JspException {
		int s = super.doAfterBody();
		if (this.innerTag != null) {
			return this.innerTag.doAfterBody();
		} else {
			return s;
		}
	}

	@Override
	public int doEndTag() throws JspException {
		int s = super.doEndTag();
		if (this.innerTag != null) {
			s = this.innerTag.doEndTag();
			this.innerTag = null;
		}
		return s;
	}

	@Override
	public void doFinally() {
		super.doFinally();
		if (this.innerTag != null) {
			this.innerTag.doFinally();
			this.innerTag = null;
		}
		this.setAccesskey(null);
		this.setDisabled(null);
		this.setReadonly(null);
		this.setItems(null);
		this.setItemLabel(null);
		this.setItemValue(null);
		this.setFirstEmpty("true");
	}

	/**
	 * Set the value of the '<code>value</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setValue(Object value) {
		Assert.notNull(value, "'value' must not be null");
		this.value = value;
	}

	/**
	 * Get the value of the '<code>value</code>' attribute. May be a runtime
	 * expression.
	 */
	protected Object getValue() {
		return this.value;
	}

	/**
	 * Set the value of the '<code>maxlength</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setMaxlength(String maxlength) {
		this.maxlength = maxlength;
	}

	/**
	 * Get the value of the '<code>maxlength</code>' attribute.
	 */
	protected String getMaxlength() {
		return this.maxlength;
	}

	/**
	 * Set the value of the '<code>alt</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setAlt(String alt) {
		this.alt = alt;
	}

	/**
	 * Get the value of the '<code>alt</code>' attribute.
	 */
	protected String getAlt() {
		return this.alt;
	}

	/**
	 * Set the value of the '<code>rows</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setRows(String rows) {
		this.rows = rows;
	}

	/**
	 * Get the value of the '<code>rows</code>' attribute.
	 */
	protected String getRows() {
		return this.rows;
	}

	/**
	 * Set the value of the '<code>cols</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setCols(String cols) {
		this.cols = cols;
	}

	/**
	 * Get the value of the '<code>cols</code>' attribute.
	 */
	protected String getCols() {
		return this.cols;
	}

	/**
	 * Is the password value to be rendered?
	 * @return <code>true</code> if the password value to be rendered.
	 */
	public boolean isShowPassword() {
		return this.showPassword;
	}

	/**
	 * Is the password value to be rendered?
	 * @param showPassword
	 *            <code>true</code> if the password value is to be rendered.
	 */
	public void setShowPassword(boolean showPassword) {
		this.showPassword = showPassword;
	}

	/**
	 * Set the value of the '<code>onselect</code>' attribute. May be a runtime
	 * expression.
	 */
	public void setOnselect(String onselect) {
		this.onselect = onselect;
	}

	/**
	 * Get the value of the '<code>onselect</code>' attribute.
	 */
	protected String getOnselect() {
		return this.onselect;
	}

	/**
	 * Set the value of the '<code>autocomplete</code>' attribute. May be a
	 * runtime expression.
	 */
	public void setAutocomplete(String autocomplete) {
		this.autocomplete = autocomplete;
	}

	/**
	 * Get the value of the '<code>autocomplete</code>' attribute.
	 */
	protected String getAutocomplete() {
		return this.autocomplete;
	}

	/**
	 * Set the value of the HTML '<code>size</code>' attribute rendered on the
	 * final '<code>select</code>' element.
	 * <p>
	 * May be a runtime expression.
	 * @param size
	 *            the desired value of the '<code>size</code>' attribute
	 */
	public void setSize(String size) {
		this.size = size;
	}

	/**
	 * Get the value of the '<code>size</code>' attribute.
	 * <p>
	 * May be a runtime expression.
	 */
	protected String getSize() {
		return this.size;
	}

	/**
	 * Set the value of the HTML '<code>multiple</code>' attribute rendered on
	 * the final '<code>select</code>' element.
	 * <p>
	 * May be a runtime expression.
	 */
	public void setMultiple(Object multiple) {
		this.multiple = multiple;
	}

	/**
	 * Get the value of the HTML '<code>multiple</code>' attribute rendered on
	 * the final '<code>select</code>' element.
	 * <p>
	 * May be a runtime expression.
	 */
	protected Object getMultiple() {
		return this.multiple;
	}

	public void setFirstEmpty(String firstEmpty) {
		this.firstEmpty = firstEmpty;
	}

	protected boolean getFirstEmpty() throws JspException {
		return this.evaluateBoolean("firstEmpty", this.firstEmpty);
	}

	// ***************************************************
	/**
	 * Set the {@link Collection}, {@link Map} or array of objects used to
	 * generate the inner '<code>option</code>' tags.
	 * <p>
	 * Required when wishing to render '<code>option</code>' tags from an array,
	 * {@link Collection} or {@link Map}.
	 * <p>
	 * Typically a runtime expression.
	 * @param items
	 *            the items that comprise the options of this selection
	 */
	public void setItems(Object items) {
		this.items = items;
	}

	/**
	 * Get the value of the '<code>items</code>' attribute.
	 * <p>
	 * May be a runtime expression.
	 */
	protected Object getItems() {
		return this.items;
	}

	/**
	 * Set the name of the property mapped to the '<code>value</code>' attribute
	 * of the '<code>option</code>' tag.
	 * <p>
	 * Required when wishing to render '<code>option</code>' tags from an array
	 * or {@link Collection}.
	 * <p>
	 * May be a runtime expression.
	 */
	public void setItemValue(String itemValue) {
		this.itemValue = itemValue;
	}

	/**
	 * Get the value of the '<code>itemValue</code>' attribute.
	 * <p>
	 * May be a runtime expression.
	 */
	protected String getItemValue() {
		return this.itemValue;
	}

	/**
	 * Set the name of the property mapped to the label (inner text) of the '
	 * <code>option</code>' tag.
	 * <p>
	 * May be a runtime expression.
	 */
	public void setItemLabel(String itemLabel) {
		this.itemLabel = itemLabel;
	}

	/**
	 * Get the value of the '<code>itemLabel</code>' attribute.
	 * <p>
	 * May be a runtime expression.
	 */
	protected String getItemLabel() {
		return this.itemLabel;
	}

	/**
	 * @return the var
	 */
	protected String getVarStatus() {
		return varStatus;
	}

	/**
	 * @param var
	 *            the var to set
	 */
	public void setVarStatus(String statusVar) {
		this.varStatus = statusVar;
	}

	/**
	 * @return the valueVar
	 */
	protected String getVarValue() {
		return varValue;
	}

	/**
	 * @param valueVar
	 *            the valueVar to set
	 */
	public void setVarValue(String valueVar) {
		this.varValue = valueVar;
	}

	/**
	 * @return the labelVar
	 */
	protected String getVarLabel() {
		return varLabel;
	}

	/**
	 * @param labelVar
	 *            the labelVar to set
	 */
	public void setVarLabel(String labelVar) {
		this.varLabel = labelVar;
	}

	/**
	 * @return the layout
	 * @throws JspException
	 */
	protected String getLayout() {
		return this.layout;
	}

	/**
	 * @param layout
	 *            the layout to set
	 */
	public void setLayout(String layout) {
		this.layout = layout;
	}

	/**
	 * @return the skipLastBody
	 * @throws JspException
	 */
	protected String getEvalLastBody() throws JspException {
		return this.evalLastBody;
	}

	/**
	 * @param skipLastBody
	 *            the skipLastBody to set
	 */
	public void setEvalLastBody(String skipEvalLastBody) {
		this.evalLastBody = skipEvalLastBody;
	}

	/**
	 * @return the type
	 * @throws JspException
	 */
	protected String getType() throws JspException {
		return this.getDisplayString(this.evaluate("type", this.type));
	}

	/**
	 * @param type
	 *            the type to set
	 */
	public void setType(String type) {
		this.type = type;
	}
}
